package com.zzg.datastructure.avl.entity;

import lombok.Data;
import lombok.ToString;

import java.util.Objects;

/**
 * @Author zhengzg
 * @Date 2022-09-30
 * @Version v1.0
 */
@Data
@ToString(of = {"value"})
public class Node {

    public int value;

    public Node left;

    public Node right;

    public Node(int value) {
        this.value = value;
    }

    // 返回左子树的高度
    public int leftHeight() {
        if (Objects.isNull(left)) {
            return 0;
        }
        return left.height();
    }

    // 返回右子树的高度
    public int rightHeight() {
        if (Objects.isNull(right)) {
            return 0;
        }
        return right.height();
    }

    // 查询高度算法
    public int height() {
        return Math.max(Objects.isNull(left) ? 0 : left.height(),Objects.isNull(right) ? 0 :right.height()) + 1;
    }

    // 左旋转方法
    /**
     * 1。左旋是因为右子树高度与左子树高度的差值大于1，那右子树应该降低高度，所以需要将根节点偏向右子树
     * 2。新节点是右子树的根节点，所以要考虑右子树根节点的左子节点A，因A一定大于原始根节点，所以新节点的右子节点指向A
     * 3.此时将新节点赋值右子树的根节点的值，并将新节点的左子节点指向原始根节点，右子节点指向原右子树根节点的右子树
     * 4.1，2，3完成此时原始根节点的右子树根节点被新节点替换，引用消失等待回收
     */
    private void leftRotate() {
        // 创建新的节点，以当前根节点的值
        Node newNode = new Node(value);
        // 把新的节点的左子树设置成当前节点的左子树
        newNode.left = left;
        // 把新节点的右子树设置成当前节点的右子树的左子树
        newNode.right = right.left;
        // 把当前节点的值替换成右子节点的值
        value = right.value;
        // 把当前节点的右子树设置成当前节点右子树的右子树
        right = right.right;
        // 把当前节点的左子树（左子节点）设置成新的节点
        left = newNode;
    }

    // 右旋转
    private void rightRotate() {
        Node newNode = new Node(value);
        newNode.right = right;
        newNode.left = left.right;
        value = left.value;
        left = left.left;
        right = newNode;
    }

    /**
     * 添加节点
     * 1.传入节点小于调用节点的左子节点，递归向左
     * 2.传入节点大于调用节点的右子节点，递归向右
     * @param node 传入节点
     */
    public void add(Node node) {
        if (Objects.isNull(node)) {
            return;
        }

        // 判断传入节点的值，和当前子树的根节点的值关系
        if (node.value < this.value) {
            // 如果当前节点左子节点为null
            if (Objects.isNull(this.left)) {
                this.left = node;
            } else {
                // 递归的向左子树添加
                this.left.add(node);
            }
        } else {
            // 添加的节点的值大于当前节点的值
            if (Objects.isNull(this.right)) {
                this.right = node;
            } else {
                // 递归向右子树添加
                this.right.add(node);
            }
        }
        // 当添加完成一个节点后，如果：（右子树的高度 - 左子树的高度） > 1 左旋转
        if (rightHeight() - leftHeight() > 1) {
            // 如果它的右子树的左子树的高度大于它的右子树的右子树的高度
            if (Objects.nonNull(right) && right.leftHeight() > right.rightHeight()) {
                // 先对右子节点进行右旋转
                right.rightRotate();
                // 然后对当前节点进行左旋转
                leftRotate();
            } else {
                // 直接左旋转
                leftRotate();
            }

            return;
        }

        // 当添加完成一个节点后，如果（左子树的高度 - 右子树的高度） > 1 右旋转
        if (leftHeight() - rightHeight() > 1) {
            // 如果他的左子树的右子树高度大于他的左子树高度
            if (Objects.nonNull(left) && left.rightHeight() > left.leftHeight()) {
                // 先对当前节点的左节点（左子树）-> 左旋转
                left.leftRotate();
                // 在对当前节点进行右旋转
                rightRotate();
            } else {
                // 直接进行右旋转
                rightRotate();
            }
        }
    }

    /**
     * 查找要删除的节点
     * @param value 要查找的值
     * @return 要删除的节点
     */
    public Node search(int value) {
        if (value == this.value) {
            return this;
        } else if (value < this.value) {
            // 如果查找的值小于当前节点，向左子树递归查找
            // 如果左子节点为空
            if (Objects.isNull(this.left)) {
                return null;
            }
            return this.left.search(value);
        } else {
            // 如果查找的值不小于当前节点，向右递归查找
            if (Objects.isNull(this.right)) {
                return null;
            }
            return this.right.search(value);
        }
    }

    /**
     * 查找父节点
     * 如果存在多个相同的数字，则查找结果只返回第一个
     * @param value 要查找的节点的值
     * @return 父节点
     */
    public Node searchParent(int value) {
        // 如果当前节就是要删除的节点的父节点，就返回
        if ((Objects.nonNull(this.left) && this.left.value == value) || (Objects.nonNull(this.right) && this.right.value == value)) {
            return this;
        } else {
            // 如果查找的值小于当前节点的值，并且当前节点的左子节点不为空
            if (value < this.value && Objects.nonNull(this.left)) {
                return this.left.searchParent(value);
            } else if (value >= this.value && Objects.nonNull(this.right)) {
                return this.right.searchParent(value);
            } else {
                // 没有找到父节点
                return null;
            }
        }
    }

    // 中序遍历
    public void infixOrder(){
        if (Objects.nonNull(this.left)) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (Objects.nonNull(this.right)) {
            this.right.infixOrder();
        }
    }
}
